package pl.edu.agh.inpema.core.dataBase.dbImplementation;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.apache.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;

import pl.edu.agh.inpema.core.dataBase.DataBase;
import pl.edu.agh.inpema.core.dataBase.dbImplementation.model.DbAnalyzeResult;
import pl.edu.agh.inpema.core.dataBase.dbImplementation.model.DbAnalyzer;
import pl.edu.agh.inpema.core.dataBase.dbImplementation.model.DbEnvironmentData;
import pl.edu.agh.inpema.core.dataBase.dbImplementation.model.DbField;
import pl.edu.agh.inpema.core.dataBase.dbImplementation.model.DbMessage;
import pl.edu.agh.inpema.core.dataBase.dbImplementation.model.DbMessageField;
import pl.edu.agh.inpema.core.dataBase.dbImplementation.model.DbReceiver;
import pl.edu.agh.inpema.core.dataBase.dbImplementation.model.DbReceiverProfile;
import pl.edu.agh.inpema.core.environment.EnvironmentInfo;
import pl.edu.agh.inpema.core.model.AnalyzeResult;
import pl.edu.agh.inpema.core.model.Message;
import pl.edu.agh.inpema.core.model.Profile;

public class DBDataBaseImpl implements DataBase {
	
	private Session session;
	
	private int environmentId;
    
    private static final Logger logger = Logger.getLogger(DBDataBaseImpl.class);
    
	public DBDataBaseImpl(Session session) {
	    this.session = session;
	    this.addField("ip");
	    this.addField("text");
	    this.addField("date");
	}
	
	public void closeConnection() {
		this.session.close();
	}
	
	public int generateNewEnvironmentId(EnvironmentInfo info) {
		DbEnvironmentData dbEnvData = new DbEnvironmentData();
		dbEnvData.setStartTime(info.getStartTime());
		dbEnvData.setEndTime(info.getEndTime());
		dbEnvData.setSuccess(info.isSuccess());
		dbEnvData.setXmlConfiguration(info.getXmlConfiguration());
		Transaction tx = session.beginTransaction();
		session.save(dbEnvData);
		tx.commit();
		info.setId(dbEnvData.getId());
		return dbEnvData.getId();		
	}
	
	public void updateEnvironment(EnvironmentInfo info) {
		Transaction tx = session.beginTransaction();
		DbEnvironmentData dbEnvData = (DbEnvironmentData) session.get(DbEnvironmentData.class, info.getId());
		dbEnvData.setStartTime(info.getStartTime());
		dbEnvData.setEndTime(info.getEndTime());
		dbEnvData.setSuccess(info.isSuccess());
		dbEnvData.setXmlConfiguration(info.getXmlConfiguration());
		session.update(dbEnvData);
		tx.commit();
	}
	
	public List<EnvironmentInfo> getAllEnvironmentInfo() {
		Transaction tx = session.beginTransaction();
		List<DbEnvironmentData> dbEnvDataList = session.createCriteria(DbEnvironmentData.class)
			.list();
		tx.commit();
		List<EnvironmentInfo> resultList = new LinkedList<EnvironmentInfo>();
		if (dbEnvDataList != null) {
			for (DbEnvironmentData dbEnvData : dbEnvDataList) {
				EnvironmentInfo info = new EnvironmentInfo();
				info.setId(dbEnvData.getId());
				info.setStartTime(dbEnvData.getStartTime());
				info.setEndTime(dbEnvData.getEndTime());
				info.setXmlConfiguration(dbEnvData.getXmlConfiguration());
				info.setSuccess(dbEnvData.isSuccess());
				resultList.add(info);
			}
		}
		return resultList;
	}
	
	public EnvironmentInfo getEnvironmentInfo(int environmentId) {
		Transaction tx = session.beginTransaction();
		DbEnvironmentData dbEnvData = (DbEnvironmentData) session.get(DbEnvironmentData.class, new Integer(environmentId));
		tx.commit();
		if (dbEnvData == null)
			return null;
		EnvironmentInfo info = new EnvironmentInfo();
		info.setId(dbEnvData.getId());
		info.setStartTime(dbEnvData.getStartTime());
		info.setEndTime(dbEnvData.getEndTime());
		info.setXmlConfiguration(dbEnvData.getXmlConfiguration());
		info.setSuccess(dbEnvData.isSuccess());
		return info;
	}
	
	public void setCurrentEnvironmentId(int environmentId) {
		this.environmentId = environmentId;
	}
	
	public synchronized void addReceiver(String receiverName) {
		DbReceiver dbReceiver = new DbReceiver(receiverName);
		dbReceiver.setEnvironmentData(this.getVersionDataFromContext());
		Transaction tx = session.beginTransaction();
		session.save(dbReceiver);
		tx.commit();
	}
	
	public synchronized void addAnalyzer(String analyzerName) {
		DbAnalyzer dbAnalyzer = new DbAnalyzer(analyzerName);
		dbAnalyzer.setEnvironmentData(this.getVersionDataFromContext());
		Transaction tx = session.beginTransaction();
		session.save(dbAnalyzer);
		tx.commit();
	}

	public synchronized Integer getProfileId(Profile profile) {
		Transaction tx = session.beginTransaction();
		List<DbReceiverProfile> profiles = session.createCriteria(DbReceiverProfile.class)
			.add(Restrictions.eq("value", profile.getValue())).createCriteria("receiver")
			.add(Restrictions.eq("name", profile.getReceiverName()))
			.createCriteria("environmentData")
			.add(Restrictions.eq("id", environmentId))
			.list();
		tx.commit();
		if (profiles != null && profiles.size() > 0) {
		    return profiles.get(0).getId();
		} else {
		    return null;
		}
	}
	
	public synchronized Integer addProfile(Profile profile) {
		DbReceiverProfile dbProfile = new DbReceiverProfile();
		DbReceiver receiver = this.getReceiverByName(profile.getReceiverName());
		dbProfile.setReceiver(receiver);
		dbProfile.setValue(profile.getValue());
		Transaction tx = session.beginTransaction();
		session.save(dbProfile);
		tx.commit();
		profile.setProfileId(dbProfile.getId());
		return dbProfile.getId();
	}
	
	public synchronized Set<Profile> getAllProfilesForReceiver(String receiverName, int environmentId) {
		Transaction tx = session.beginTransaction();
		List<DbReceiverProfile> profiles = session.createCriteria(DbReceiverProfile.class)
			.createCriteria("receiver")
			.add(Restrictions.eq("name", receiverName))
			.createCriteria("environmentData")
			.add(Restrictions.eq("id", environmentId))
			.list();
		tx.commit();
		Set<Profile> resultSet = new HashSet<Profile>();
		if (profiles != null) {
			for (DbReceiverProfile dbProfile : profiles) {
				Profile profile = new Profile();
				profile.setProfileId(dbProfile.getId());
				profile.setReceiverName(receiverName);
				profile.setValue(dbProfile.getValue());
				resultSet.add(profile);
			}
		}
		return resultSet;
	}
	
	public synchronized int addMessage(Message message) {
		
		DbMessage dbMessage = new DbMessage();
		DbReceiverProfile profile = this.getProfileById(message.getProfileId());
		//logger.info(profile);
		dbMessage.setProfile(profile);
		profile.getDbMessages().add(dbMessage);
		
		for (String fieldName : message.getFieldNames()) {
			String fieldValue = message.getField(fieldName);
			DbMessageField messageField = new DbMessageField();
			messageField.setField(getFieldByName(fieldName));
			messageField.setValue(fieldValue);
			messageField.setMessage(dbMessage);
			dbMessage.getMessageFields().add(messageField);
		}
		Transaction tx = session.beginTransaction();
		session.save(dbMessage);
		//logger.info("ADD MESSAGE "+dbMessage.getId());
		tx.commit();
		return dbMessage.getId();
	}

	public synchronized Message getMessage(int messageId) {
		//logger.info("GET MESSAGE "+messageId);
		Transaction tx = session.beginTransaction();
		DbMessage dbMessage = (DbMessage) session.get(DbMessage.class, new Integer(messageId));
		tx.commit();
		if (dbMessage == null)
			return null;
		Message message = new Message(dbMessage.getProfile().getId());
		for (Object dbMessageFieldObj : dbMessage.getMessageFields()) {
			DbMessageField dbMessageField = (DbMessageField) dbMessageFieldObj;
			message.setField(dbMessageField.getField().getName(), dbMessageField.getValue());
		}
		return message;
	}
	
	public synchronized Set<Integer> getAllMessagesIdForProfile(int profileId) {
		DbReceiverProfile profile = getProfileById(profileId);
		Set<Integer> resultSet = new HashSet<Integer>();
		for (Object messageObj : profile.getDbMessages()) {
			DbMessage message = (DbMessage) messageObj;
			resultSet.add(message.getId());
		}
		return resultSet;
	}
	
	public synchronized Integer getAnalyzeResultId(String analyzerName, int profileId) {
		Transaction tx = session.beginTransaction();
		Criteria analyzerResultCriteria = session.createCriteria(DbAnalyzeResult.class); 
		analyzerResultCriteria
			.createCriteria("dbAnalyzer")
			.add(Restrictions.eq("name", analyzerName))
			.createCriteria("environmentData")
			.add(Restrictions.eq("id", environmentId));
		analyzerResultCriteria.createCriteria("dbReceiverProfile")
			.add(Restrictions.eq("id", profileId));
		List<DbAnalyzeResult> results = analyzerResultCriteria.list();
		tx.commit();
		if (results != null && results.size() > 0) {
		    return results.get(0).getId();
		} else {
		    return null;
		}
	}
	
	public synchronized Integer addAnalyzeResultId(String analyzerName, int profileId) {
		DbAnalyzeResult dbAnalyzeResult = new DbAnalyzeResult();
		DbAnalyzer analyzer = getAnalyzerByName(analyzerName);
		dbAnalyzeResult.setDbAnalyzer(analyzer);
		analyzer.getDbAnalyzeResults().add(dbAnalyzeResult);
		DbReceiverProfile profile = getProfileById(profileId); 
		//logger.info("AAA"+profile.getId());
		dbAnalyzeResult.setDbReceiverProfile(profile);
		profile.getDbAnalyzeResults().add(dbAnalyzeResult);
		Transaction tx = session.beginTransaction();
		session.save(dbAnalyzeResult);
		tx.commit();
		return dbAnalyzeResult.getId();
	}
	
	public synchronized Set<AnalyzeResult> getAllAnalyzeResults(String analyzerName, int envId) {
		Transaction tx = session.beginTransaction();
		Criteria analyzerResultCriteria = session.createCriteria(DbAnalyzeResult.class); 
		analyzerResultCriteria
			.createCriteria("dbAnalyzer")
			.add(Restrictions.eq("name", analyzerName))
			.createCriteria("environmentData")
			.add(Restrictions.eq("id", envId));
		List<DbAnalyzeResult> results = analyzerResultCriteria.list();
		tx.commit();
		Set<AnalyzeResult> resultSet = new HashSet<AnalyzeResult>();
		for (DbAnalyzeResult dbAnalyzeResult : results) {
			AnalyzeResult result = new AnalyzeResult();
			result.setAnalyzeResultDataId(dbAnalyzeResult.getId());
			result.setAnalyzerName(dbAnalyzeResult.getDbAnalyzer().getName());
			result.setProfileId(dbAnalyzeResult.getDbReceiverProfile().getId());
			result.setProfileValue(dbAnalyzeResult.getDbReceiverProfile().getValue());
			result.setReceiverName(dbAnalyzeResult.getDbReceiverProfile().getReceiver().getName());
			resultSet.add(result);
		}
		return resultSet;
	}
	
	public synchronized int getLastVersionData() {
		Transaction tx = session.beginTransaction();
		List<DbEnvironmentData> versionDatas = session.createCriteria(DbEnvironmentData.class)
			.addOrder(Order.desc("startTime"))
			.list();
		tx.commit();
		if (versionDatas != null && versionDatas.size() > 0) {
		    return versionDatas.get(0).getId();
		} else {
		    return 0;
		}
	}
	
	public synchronized void addResultObject(String analyzerName, Object resultObject) {
		Transaction tx = session.beginTransaction();
		session.saveOrUpdate(resultObject);
		tx.commit();
	}
	
	public synchronized List<Object> getResultObject(int resultId, Class objectClass) {
		Transaction tx = session.beginTransaction();
		List<Object> results = session.createCriteria(objectClass)
			.add(Restrictions.eq("resultId", resultId))
			.list();
		tx.commit();
		return results;
		//return null;
	}
	
	private DbEnvironmentData getVersionDataFromContext() {
		Transaction tx = session.beginTransaction();
		DbEnvironmentData versionData = (DbEnvironmentData) session.get(DbEnvironmentData.class, new Integer(this.environmentId));
		tx.commit();
		return versionData;
	}
	
	private DbReceiver getReceiverByName(String receiverName) {
		Transaction tx = session.beginTransaction();
		List<DbReceiver> receivers = session.createCriteria(
			DbReceiver.class).add(Restrictions.eq("name", receiverName)).createCriteria("environmentData")
			.add(Restrictions.eq("id", this.environmentId)).list();
		tx.commit();
		if (receivers != null && receivers.size() > 0) {
		    return receivers.get(0);
		} else {
		    return null;
		}
	}
	
	private DbAnalyzer getAnalyzerByName(String analyzerName) {
		Transaction tx = session.beginTransaction();
		List<DbAnalyzer> analyzers = session.createCriteria(
				DbAnalyzer.class).add(Restrictions.eq("name", analyzerName)).createCriteria("environmentData")
			.add(Restrictions.eq("id", this.environmentId)).list();
		tx.commit();
		if (analyzers != null && analyzers.size() > 0) {
		    return analyzers.get(0);
		} else {
		    return null;
		}
	}
	
	private DbReceiverProfile getProfileById(int id) {
		Transaction tx = session.beginTransaction();
		DbReceiverProfile profile = (DbReceiverProfile) session.get(DbReceiverProfile.class, new Integer(id));
		tx.commit();
		return profile;
	}
	
	private DbField getFieldByName(String name) {
		Transaction tx = session.beginTransaction();
		List<DbField> messageFields = session.createCriteria(
				DbField.class).add(Restrictions.eq("name", name)).list();
		tx.commit();
		if (messageFields != null && messageFields.size() > 0) {
		    return messageFields.get(0);
		} else {
		    return null;
		}
	}
	
	private void addField(String fieldName) {
		DbField field = new DbField();
		field.setName(fieldName);
		Transaction tx = session.beginTransaction();
		List<DbField> dbFields = session.createCriteria(
				DbField.class).add(Restrictions.eq("name", fieldName)).list();
		if (dbFields == null || dbFields.size() == 0)
			session.save(field);
		tx.commit();
	}
}
